///////////////////////////////////////////////////////////////////////////////
//
//mmQuat.h
// 
// Author:
//      Mark Hobbs mark@hobsie.com
// 
// Copyright (c) 2010 Mark Hobbs
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
///////////////////////////////////////////////////////////////////////////////

#ifndef _MM_QUAT_H_
#define _MM_QUAT_H_

#include "mmDefines.h"
#include "mmVector3.h"
#include <math.h>

namespace MM
{

#ifdef _MSC_VER
__declspec(align(16)) class Quat
#else
class Quat
#endif
{
public:

	union
	{
		struct
		{
			float x,y,z,w;
		};
		float f[4];
	};
	
	inline Quat();
	inline Quat(float inputX, float inputY, float inputZ, float inputW);
	inline Quat(const Vector3 &vector);
	inline Quat(const Quat &quat);
	
    inline Quat operator + (const Quat &quat);
    inline Quat operator - (const Quat &quat);
    inline Quat operator * (const Quat &quat);
    
    inline void operator += (const Quat &quat);
    inline void operator -= (const Quat &quat);
    
    inline float getMagnitude();
    inline float getSqrMagnitude();
    inline void  normalise();
    inline void  conjugate();

#ifdef __GNUC__
} __attribute__ ((aligned(16)));
#else
}; // class Quat
#endif

// Inline function defintions
inline Quat::Quat()
{
}
		
inline Quat::Quat(float inputX, float inputY, float inputZ, float inputW)
{
	x = inputX;
	y = inputY;
	z = inputZ;
	w = inputW;
}

inline Quat::Quat(const Vector3 &vector)
{
  x = vector.x;
  y = vector.y;
  z = vector.z;
  w = 0.0f;
}

inline Quat::Quat(const Quat &quat)
{
	x = quat.x;
	y = quat.y;
	z = quat.z;
	w = quat.w;
}

inline Quat Quat::operator + (const Quat &quat)
{
	Quat temp;
	temp.x = x + quat.x;
	temp.y = y + quat.y;
	temp.z = z + quat.z;
	temp.w = w + quat.w;
	return temp;
}

inline Quat Quat::operator - (const Quat &quat)
{
	Quat temp;
	temp.x = x - quat.x;
	temp.y = y - quat.y;
	temp.z = z - quat.z;
	temp.w = w - quat.w;
	return temp;
}

inline Quat Quat::operator * (const Quat &quat)
{
	Quat temp;
    temp.w = w*quat.w - x*quat.x - y*quat.y - z*quat.z;
    temp.x = w*quat.x + x*quat.w + y*quat.z - z*quat.y;
    temp.y = w*quat.y + y*quat.w + z*quat.x - x*quat.z;
    temp.z = w*quat.z + z*quat.w + x*quat.y - y*quat.x;
	return temp;
}

inline void Quat::operator += (const Quat &quat)
{
	x += quat.x;
	y += quat.y;
	z += quat.z;
	w += quat.w;
}

inline void Quat::operator -= (const Quat &quat)
{
  	x -= quat.x;
	y -= quat.y;
	z -= quat.z;
	w -= quat.w;
}

inline float Quat::getMagnitude()
{
	return sqrtf(getSqrMagnitude());
}

inline float Quat::getSqrMagnitude()
{
    return ((w * w)+(x * x)+(y * y)+(z * z));
}

inline void Quat::normalise()
{
	float length = getMagnitude();
	
	if(length <= MM_MIN_VALUE)
		return; // Value too small to produce accurate results
		
	x = x / length;
	y = y / length;
	z = z / length;
	w = w / length;
}

inline void Quat::conjugate()
{
	x = -x;
	y = -y;
	z = -z;
}
	
} // namespace MM

#endif // _MM_QUAT_H_
